前情提要:
昨天安裝了react router以及介紹了裡面登場的角色,今天就來套用在我們的萬年好朋友todos上吧!
一開始覺得很廢的todos,現在我要好好感謝你。
如果不是你,怎麼來驗證實作?
如果不是你,我去哪邊找現成專案給我玩?
感謝todos,我又可以對你毛手毛腳了。 (≧▽≦)
有鑒於之前看到了presentational和container components的區分,
看了Dan Abramov分享的這篇後,
抱著好奇和嘗試的心調整了一下,把layout和邏輯拆開,拆完後又自我懷疑了
在渲染TodoList這塊覺得拆開挺好的
但在新建todos的NewTodo覺得好像不是很需要,雖然我還是拆了它
是真的需要拆開呢?還是我只是為拆而拆?大概需要再多做幾個練習驗證XD
詳細內容可以參考github免得又佔版面惹
拆完之後就心無罣礙的開始安裝react router吧
yarn add react-router-dom
因為不想讓專案目錄下的index.js變得越來越複雜,所以另外新增了一個router.js檔案,建立RootRouter component,並且把有關router的設定都放在這邊。
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Layout from "./src/components/Layout";
import Home from "./src/components/Home";
import Todos from "./src/components/Todos";
import News from "./src/components/News";
const RootRouter = () => {
return (
<Router>
<Layout>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/todos" component={Todos} />
<Route path="/news" component={News} />
</Switch>
</Layout>
</Router>
);
};
export default RootRouter;
看完上面的router.js可能會覺得有點不對勁
疑惑Layout component是哪裡跑來插花的角色,其實Layout只是React的Composition(組合)。
router.js內<Layout>JSX中的任何内容都會被傳到Layout component中作為children props傳入,所以透過這樣組合的方式,我可以把Switch內容作為childrend傳入Layout使用,可以把它想像成畫框的感覺,Layout就是那個框,我只要匹配pathname就可以把內容component置換。
講完一個疑點後還有另一個**Fragments**,Fragments的功能就是幫我聚集多個react子元素,而且不會在DOM裡面增加節點的好幫手。
也就是說,我想要把Header和prop.children都渲染但不想用<div>把它們圈起來,因為<div>在這裡沒有任何功能,這時候就可以使用Fragment達到我的期望。
另外,<Fragment>其實還可以透過JSX的語法糖簡化成<>
import React, { Fragment } from "react";
import Header from "./Header";
const Layout = props => {
return (
<Fragment>
<Header />
{props.children}
</Fragment>
);
};
export default Layout;
接著,我打算在header放上導覽列Link到各個頁面,所以在Header component這邊,我從react-router-dom匯入了NavLink component,大家還記得他跟Link component差異在哪嗎?就是多了activeClassName可以用。
因為className設很長不想重複輸入,所以我把導覽列的項目資訊先存在navList裡,to是指匹配的path;text則是指導覽列的顯示文字。
import React from 'react';
import { NavLink } from "react-router-dom";
const navList = [
{ to: "/", text: "Home" },
{ to: "/todos", text: "Todos" },
{ to: "/news", text: "News" },
]
const Header = () => {
return (
<header className="header">
<h1 className="header-heading">Heading</h1>
<nav>
<ul className="header-nav-list">
{ navList.map(({to, text}, index) => (
<li key={index} className="header-nav-list-item">
<NavLink
exact
className="list-item-anchor"
activeClassName="list-item-anchor-active"
to={to}>
{text}
</NavLink>
</li>
))}
</ul>
</nav>
</header>
);
};
export default Header;
接著,我就可以開始建立那三頁(home, todos和news)要render的component,因為只有todos已經好了,所以其他頁面先很賤簡單的渲染標題吧!Home和News component目前都是一樣的狀態就不放上來佔空間了。
import React from 'react';
const Home = () => {
return (
<main className="main main-no-content">
Home
</main>
);
};
export default Home;
全部完成後,回到專案目錄的index.js匯入RootRouter component,並把Provider內的component改成RootRouter就大功告成了~
import RootRouter from "./router";
ReactDOM.render(
<Provider store={store}>
<RootRouter />
</Provider>,
document.getElementById("app")
);
加上一些CSS的設定後實際操作如下:
可以看到透過NavLink產生的anchor可以切換不同path渲染不同的component。
因為NavLink有設定activeClassName的關係,所以我不需要判斷現在的pathname是什麼,就可以把該頁的nav link加上特定的className。
今日總結:
今天把react router的主要角色跟todos串接結合了,明天再來看看他們可以譜出什麼火花~